package com.zxy.ziems.server.energy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zxy.btp.common.response.PageResult;
import com.zxy.btp.redis.utils.RedisUtil;
import com.zxy.ziems.server.constant.enums.DictCodeEnum;
import com.zxy.ziems.server.device.service.DataTypeService;
import com.zxy.ziems.server.domain.DictItem;
import com.zxy.ziems.server.energy.convertor.EnergyPriceConvertor;
import com.zxy.ziems.server.energy.mapper.EnergyPriceMapper;
import com.zxy.ziems.server.energy.pojo.dto.EnergyPriceDTO;
import com.zxy.ziems.server.energy.pojo.entity.EnergyPriceEntity;
import com.zxy.ziems.server.energy.pojo.param.EnergyPriceQueryParam;
import com.zxy.ziems.server.energy.pojo.vo.EnergyPriceVO;
import com.zxy.ziems.server.energy.service.EnergyPriceService;
import com.zxy.ziems.server.energy.service.EnergyPriceTimeService;
import com.zxy.ziems.server.log.DataRecordTypeEnum;
import com.zxy.ziems.server.log.pojo.entity.DataRecordLogEntity;
import com.zxy.ziems.server.log.service.DataRecordService;
import com.zxy.ziems.server.mybatis.core.service.ServiceImplX;
import com.zxy.ziems.server.service.DictItemService;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.BusinessExceptionUtil;
import com.zxy.ziems.server.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.zxy.ziems.server.constant.RedisKeyConstant.GET_DATA_TYPE_PRICE_;

@Service
public class EnergyPriceServiceImpl extends ServiceImplX<EnergyPriceMapper, EnergyPriceEntity> implements EnergyPriceService {

    @Autowired
    private DataTypeService dataTypeService;
    @Autowired
    private DictItemService dictItemService;
    @Autowired
    private EnergyPriceTimeService energyPriceTimeService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DataRecordService dataRecordService;

    @Transactional
    @Override
    public void add(EnergyPriceDTO energyPriceDTO) {

        doCheckBeforeSaveAction(energyPriceDTO);

        EnergyPriceEntity energyPriceEntity = EnergyPriceConvertor.INSTANT.dto2Entity(energyPriceDTO);

        save(energyPriceEntity);
    }

    private void doCheckBeforeSaveAction(EnergyPriceDTO energyPriceDTO) {
        AssertUtil.nonNull(dataTypeService.getById(energyPriceDTO.getDataTypeId()), "未找到能源类型");

        List<EnergyPriceEntity> energyPriceEntities = queryByDataTypeId(energyPriceDTO.getDataTypeId());
        if (CollectionUtils.isEmpty(energyPriceEntities)) {
            return;
        }
        if(energyPriceDTO.getPeakStatus() && energyPriceDTO.getSingleStatus()) {
            throw BusinessExceptionUtil.exception("单一价格和每天分段价格不能同时配置");
        }

        for (EnergyPriceEntity energyPriceEntity : energyPriceEntities) {
            if (energyPriceDTO.getStartDate().compareTo(energyPriceEntity.getStartDate()) > -1
                    && energyPriceDTO.getStartDate().compareTo(energyPriceEntity.getEndDate()) < 1
                    || energyPriceDTO.getEndDate().compareTo(energyPriceEntity.getStartDate()) > -1
                    && energyPriceDTO.getEndDate().compareTo(energyPriceEntity.getEndDate()) < 1) {
                throw BusinessExceptionUtil.exception("日期已配置不可重复配置");
            }
        }
        energyPriceDTO.setSinglePrice(energyPriceDTO.getSingleStatus() ? energyPriceDTO.getSinglePrice() : null);

    }

    @Override
    public List<EnergyPriceEntity> queryByDataTypeId(String dataTypeId) {
        return baseMapper.selectList(EnergyPriceEntity::getDataTypeId, dataTypeId);
    }

    @Transactional
    @Override
    public void modify(EnergyPriceDTO energyPriceDTO) {
        doCheckBeforeModifyAction(energyPriceDTO);

        EnergyPriceEntity energyPriceEntity = EnergyPriceConvertor.INSTANT.dto2Entity(energyPriceDTO);

        removeCache();
        addLog(energyPriceEntity);

        updateById(energyPriceEntity);
    }

    private void addLog(EnergyPriceEntity energyPriceEntity) {
        EnergyPriceEntity old = baseMapper.selectById(energyPriceEntity.getId());
        if (energyPriceEntity.getSingleStatus() && energyPriceEntity.getSinglePrice() != null
                && old.getSinglePrice().compareTo(energyPriceEntity.getSinglePrice()) != 0) {
            List<DataRecordLogEntity> logs = new ArrayList<>();
            DataRecordLogEntity log = new DataRecordLogEntity();
            log.setTag("单一价格");
            log.setOldData(old.getSinglePrice().toString());
            log.setNewData(energyPriceEntity.getSinglePrice().toString());
            logs.add(log);
            dataRecordService.add(DataRecordTypeEnum.ENERGY_PRICE_MODIFY, energyPriceEntity.getName(), energyPriceEntity.getId(), logs);
        }
    }

    private void removeCache() {
        Set<String> keys = redisTemplate.keys(GET_DATA_TYPE_PRICE_ + "*");
        redisTemplate.delete(keys);
    }

    private void doCheckBeforeModifyAction(EnergyPriceDTO energyPriceDTO) {
        AssertUtil.nonNull(dataTypeService.getById(energyPriceDTO.getDataTypeId()), "未找到能源类型");

        List<EnergyPriceEntity> energyPriceEntities = queryByDataTypeId(energyPriceDTO.getDataTypeId());
        if (CollectionUtils.isEmpty(energyPriceEntities)) {
            return;
        }
        for (EnergyPriceEntity energyPriceEntity : energyPriceEntities) {
            if (energyPriceEntity.getId().equals(energyPriceDTO.getId())) continue;
            if (energyPriceDTO.getStartDate().compareTo(energyPriceEntity.getStartDate()) > -1
                    && energyPriceDTO.getStartDate().compareTo(energyPriceEntity.getEndDate()) < 1
                    || energyPriceDTO.getEndDate().compareTo(energyPriceEntity.getStartDate()) > -1
                    && energyPriceDTO.getEndDate().compareTo(energyPriceEntity.getEndDate()) < 1) {
                throw BusinessExceptionUtil.exception("日期已配置不可重复配置");
            }
        }

        if (!energyPriceDTO.getSingleStatus()) {
            energyPriceDTO.setSinglePrice(null);
        }
    }

    @Override
    public PageResult<EnergyPriceVO> page(EnergyPriceQueryParam energyPriceParam) {
        PageResult<EnergyPriceEntity> energyPriceEntityPageResult = customPage(energyPriceParam, (page, aParam) -> baseMapper.queryPageList(page, aParam));

        List<DictItem> dictItems = dictItemService.getByDictCode(DictCodeEnum.ENERGY_TYPE.getCode());
        Map<String, String> dataType = dictItems.stream().collect(Collectors.toMap(DictItem::getItemCode, DictItem::getItemName));
        List<EnergyPriceVO> rest = energyPriceEntityPageResult.getList().stream().map(k -> {
            EnergyPriceVO energyPriceVO = EnergyPriceConvertor.INSTANT.entity2Vo(k);
            energyPriceVO.setDataTypeName(dataType.get(energyPriceVO.getDataTypeId()));
            energyPriceVO.setUpdateTime(k.getUpdateTime());
            return energyPriceVO;
        }).collect(Collectors.toList());
        return new PageResult<>(energyPriceEntityPageResult.getTotal(), rest);
    }

    @Transactional
    @Override
    public void remove(String id) {
        EnergyPriceEntity entity = getById(id);
        if (entity == null) return;

        removeCache();
        // 删除时段
        energyPriceTimeService.removeByEnergyPriceId(id);
        removeById(id);
    }

    @Override
    public BigDecimal getEnergyPriceByDataTypeId(String dataTypeId, LocalDateTime localDateTime) {
        String key = GET_DATA_TYPE_PRICE_ + dataTypeId + localDateTime.toString();
        BigDecimal res = (BigDecimal) redisUtil.get(key);
        if (res != null) return res;
        QueryWrapper<EnergyPriceEntity> param = new QueryWrapper<>();
        param.lambda().eq(EnergyPriceEntity::getDataTypeId, dataTypeId)
                .le(EnergyPriceEntity::getStartDate, localDateTime)
                .ge(EnergyPriceEntity::getEndDate, localDateTime);
        EnergyPriceEntity entity = baseMapper.selectOne(param);

        if (entity == null) {
            res = null;
        } else if (entity.getSingleStatus()) {
            res = entity.getSinglePrice();
        } else {
            res = energyPriceTimeService.getEnergyPrice(entity.getId(), localDateTime);
        }
        redisUtil.set(key, res, 5L, TimeUnit.MINUTES);
        return res;
    }
}
